package com.booter.hessian.component;

import com.booter.hessian.annotation.HessianReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.remoting.caucho.HessianProxyFactoryBean;
import org.springframework.util.ClassUtils;

/***
 * 将注解为HessianClientService的服务发布为spring Hessian 客户端bean
 * 首先过滤出服务接口 .真正项目中，服务接口会单独提供jar
 * 然后构建HessianProxyFactoryBean
 * 最后发布服务client服务
 *
 * @author lcw
 */
@Slf4j
public class HessianClientServiceExporterFactory implements ApplicationContextAware {

    private final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    private String pattern = "";
    private String host = "";

    public HessianClientServiceExporterFactory(String pkg, String host) {
        this.pattern = this.pattern
                .concat(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX)
                .concat(ClassUtils.convertClassNameToResourcePath(pkg))
                .concat("/**/*.class");
        this.host = host;
    }

    @Override
    public void setApplicationContext(ApplicationContext context)
            throws BeansException {
        try {
            //在类路径下过滤出所有class
            Resource[] resources = resourcePatternResolver.getResources(this.pattern);
            log.info("Pattern:" + this.pattern);
            MetadataReaderFactory metaReader = new CachingMetadataReaderFactory(this.resourcePatternResolver);
            ConfigurableApplicationContext cac = (ConfigurableApplicationContext) context;
            DefaultListableBeanFactory bf = (DefaultListableBeanFactory) cac.getAutowireCapableBeanFactory();
            for (Resource resource : resources) {
                if (!resource.isReadable()) {
                    continue;
                }
                MetadataReader reader = metaReader.getMetadataReader(resource);
                if (!reader.getClassMetadata().isInterface()) {
                    continue;
                }

                String className = reader.getClassMetadata().getClassName();
                Class<?> clazz = ClassUtils.forName(className, Thread.currentThread().getContextClassLoader());
                if (clazz.getAnnotation(HessianReference.class) != null) {
                    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(HessianProxyFactoryBean.class);
                    //By default the value is false. As a matter of fact, it doesn't influence the test result, the overloaded method still be supported
                    builder.addPropertyValue("overloadEnabled", false);
                    builder.addPropertyValue("serviceUrl", this.host.concat("/").concat(toLowerCase(clazz.getSimpleName())));
                    builder.addPropertyValue("serviceInterface", className);
                    bf.registerBeanDefinition(toLowerCase(clazz.getSimpleName()), builder.getBeanDefinition());
                    log.info("serviceUrl:" + this.host.concat("/").concat(toLowerCase(clazz.getSimpleName())) + " serviceInterface:" + className);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /***
     * 首字母小写
     *
     * @param str
     * @return
     */
    private static String toLowerCase(String str) {
        return new StringBuilder(str)
                .replace(0, 1, String.valueOf(str.charAt(0)).toLowerCase())
                .toString();
    }

}
